#include <iostream>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

using namespace std;

#define PATHNAME "." // 项目名
#define PROJID 0x29c // 项目编号

enum
{
    SERVER = 0,
    CLIENT = 1
};

class shm
{
public:
    shm(int id)
        : _id(id)
    {
        _key = getKey(); // 获取key

        // 根据不同的身份，创建 / 打开 共享内存
        if (_id == SERVER)
            _shmid = shmHelper(_key, gsize, IPC_CREAT | IPC_EXCL | mode);
        else
            _shmid = shmHelper(_key, gsize, IPC_CREAT);

        //关联共享内存
        _start = shmat(_shmid,NULL,0); //关联
        if(_start == (void*) -1)
        {
            cerr << "shmat fail!"
                 << "errno: " << errno << " | " << strerror(errno) << endl;
            exit(1);

        }
    }

    ~shm()
    {
        //去关联
        int n =shmdt(_start);
        if(n == -1)
        {
            cerr << "shmdt fail!"
                 << "errno: " << errno << " | " << strerror(errno) << endl;
            exit(1);
        }

        // 根据不同的身份，判断是否需要删除共享内存
        if(_id == SERVER)
         shmctl(_shmid,IPC_RMID,NULL);
    }

    key_t getKey() const
    {
        key_t key = ftok(PATHNAME,PROJID);
        if(key == -1)
        {
            // 失败，终止进程
            cerr << "ftok fail!  "
                 << "errno: " << errno << " | " << strerror(errno) << endl;
            exit(1);
        }
        return key;
    }

    int getShmID() const
    {
        return _shmid;
    }

    void* getStart() const
    {
        return _start;
    }

protected:
    static const int gsize =4096;
    static const mode_t mode = 0666;

    //将十进制转换为十六进制数
    string toHEX(int x)
    {
        char buffer[64];
        snprintf(buffer,sizeof(buffer),"0x%x",x);
        return buffer;
    }

    //共享内存助手
    int shmHelper(key_t key,size_t size,int flags)
    {
        int shmid =shmget(key,size,flags);
        if(shmid == -1)
        {
            // 失败，终止进程
            cerr << "shmget fail!  "
                 << "errno: " << errno << " | " << strerror(errno) << endl;
            exit(2);

        }
        return shmid;
    }



private:
    key_t _key;
    int _shmid = 0;
    void *_start;
    int _id; // 身份标识符，用来区分服务端与客户端
};
